在 WPF MVVM Application 中使用 Dependency Injection
TLDR
- 在 WPF 中引入
Microsoft.Extensions.DependencyInjection可實現現代化的依賴注入,取代傳統的 Singleton 或手動管理。 - 使用
CommunityToolkit.Mvvm可簡化 MVVM 架構開發,透過 Source Generators 自動產生屬性與指令。 - 必須移除
App.xaml中的StartupUri屬性,改由OnStartup手動建立MainWindow,否則會因建構函式參數注入失敗而報錯。 - ViewModel 需繼承
ObservableObject並宣告為partialclass,以便使用[ObservableProperty]與[RelayCommand]屬性。 - 綁定時需注意命名慣例:
[ObservableProperty]產生的屬性為 Pascal Case,[RelayCommand]產生的指令需加上Command後綴。
在 WPF 使用 Dependency Injection
在 WPF 專案中引入 DI 容器(如 Microsoft.Extensions.DependencyInjection)時,必須調整應用程式的啟動流程。
App.xaml 設定與啟動邏輯
什麼情況下會遇到這個問題:當你將 MainWindow 改為透過 DI 容器注入依賴(例如在建構函式中注入 ViewModel)時。
由於 WPF 預設的 StartupUri 機制會嘗試呼叫無參數的建構函式,若改用 DI 建立 MainWindow,必須移除 App.xaml 中的 StartupUri 屬性,並在 App.xaml.cs 的 OnStartup 方法中手動初始化:
csharp
public partial class App : Application {
protected override void OnStartup(StartupEventArgs e) {
IConfigurationBuilder builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
IConfiguration configuration = builder.Build();
ServiceCollection serviceCollection = new ServiceCollection();
ConfigureServices(serviceCollection, configuration);
ServiceProvider serviceProvider = serviceCollection.BuildServiceProvider();
MainWindow mainWindow = serviceProvider.GetRequiredService<MainWindow>()!;
mainWindow.Show();
}
private static void ConfigureServices(IServiceCollection services, IConfiguration configuration) {
services.Configure<AppOptions>(configuration!.GetSection("App"));
services.AddTransient<MainWindow>();
services.AddTransient<ViewModel>();
}
}建立 WPF MVVM Application
使用 CommunityToolkit.Mvvm 可以大幅減少樣板程式碼。
ViewModel 實作規範
什麼情況下會遇到這個問題:當你需要實作資料綁定與指令,但不想手寫繁瑣的 INotifyPropertyChanged 實作時。
ViewModel 必須符合以下規範:
- 繼承
ObservableObject。 - 宣告為
partialclass。 - 使用
[ObservableProperty]標記欄位(Source Generator 會自動產生對應的 Pascal Case 屬性)。 - 使用
[RelayCommand]標記方法(Source Generator 會自動產生對應的Command屬性)。
csharp
public partial class ViewModel : ObservableObject {
[ObservableProperty]
private string? input;
[RelayCommand]
private void Submit() {
MessageBox.Show("輸入值:" + Input);
Input += "_修改";
MessageBox.Show("變更值:" + Input);
}
}

MainWindow 綁定設定
什麼情況下會遇到這個問題:當你在 XAML 中進行資料綁定時,若未遵循自動產生的命名規則,會導致綁定失敗。
在 MainWindow 的建構函式中注入 ViewModel 並設定 DataContext:
csharp
public partial class MainWindow : Window {
public MainWindow(ViewModel viewModel) {
InitializeComponent();
DataContext = viewModel;
}
}在 XAML 中,綁定名稱需對應 Source Generator 產生的結果:
TextBox綁定Input(對應input欄位)。Button綁定SubmitCommand(對應Submit方法)。
xml
<Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<TextBox Text="{Binding Input}"/>
<Button Content="送出" Command="{Binding SubmitCommand}"/>
</Grid>
</Window>執行結果
- 點擊「送出」後,
Submit()方法成功執行並取得Input值。 - 當
Submit()修改Input屬性時,畫面的TextBox會自動同步更新。



異動歷程
- 初版文件建立。